package com.genericdevice.platform.core.plugins;

import com.genericdevice.platform.core.data.DataBase;
import com.genericdevice.platform.core.data.QueryHelper;
import com.genericdevice.platform.core.logging.LoggerHelper;
import dalvik.system.PathClassLoader;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Xilent on 18.05.2014.
 */
public class PluginsLoader {

    private static Map<String, Class<?>> pluginResultFormat = new HashMap<String, Class<?>>();
    private static List<Map<String, Object>> pluginsList;

    static {
        pluginResultFormat.put("_id", Integer.class);
        pluginResultFormat.put(DataBase.PLUGINS_NAME_FIELD, String.class);
        pluginResultFormat.put(DataBase.PLUGINS_DESC_FIELD, String.class);
        pluginResultFormat.put(DataBase.PLUGINS_PATH_FIELD, String.class);
        pluginResultFormat.put(DataBase.PLUGINS_CLASS_FIELD, String.class);
        pluginResultFormat.put(DataBase.PLUGINS_DEFAULT_FIELD, Integer.class);
    }

    public static IPlugin loadSelectedPlugin() {
        if (pluginsList == null)
            updatePluginsList();

        Map<String, Object> plugin = null;
        for (Map<String, Object> pluginEntry : pluginsList) {
            if ((Integer)pluginEntry.get(DataBase.PLUGINS_DEFAULT_FIELD) == 1) {
                plugin = pluginEntry;
                break;
            }
        }

        if (plugin == null)
            return null;

        LoggerHelper.getInstance().log("Found plugin: " + plugin.get(DataBase.PLUGINS_CLASS_FIELD));

        URL pathUrl;
        try {
            pathUrl = new URL("jar", "", "file://" + plugin.get(DataBase.PLUGINS_PATH_FIELD) + "!/");
        } catch (MalformedURLException e) {
            LoggerHelper.getInstance().error("Cant load path: " + plugin.get(DataBase.PLUGINS_PATH_FIELD), e);
            return null;
        }

        URLClassLoader classLoader = new URLClassLoader(new URL[]{pathUrl}, ClassLoader.getSystemClassLoader());

        Class<?> pluginClass;
        try {
            pluginClass = Class.forName((String) plugin.get(DataBase.PLUGINS_CLASS_FIELD), false, classLoader);
        } catch (ClassNotFoundException e) {
            LoggerHelper.getInstance().error("Cant load class: " + plugin.get(DataBase.PLUGINS_CLASS_FIELD), e);
            return null;
        }

        try {
            return (IPlugin) pluginClass.newInstance();
        } catch (Exception e) {
            LoggerHelper.getInstance().error("Cant get new instance of class: " + plugin.get(DataBase.PLUGINS_CLASS_FIELD), e);
            return null;
        }
    }

    public static List<Map<String, Object>> getPluginsList() {
        if (pluginsList == null)
            updatePluginsList();

        return pluginsList;
    }

    public static void addPlugin(String path) {
        for (Map<String, Object> plugin : pluginsList) {
            if (plugin.get(DataBase.PLUGINS_PATH_FIELD).equals(path))
                return;
        }

        QueryHelper query = new QueryHelper();
        DataBase db = DataBase.getInstance();

        URL pathUrl;
        try {
            pathUrl = new URL("file://" + path);
        } catch (MalformedURLException e) {
            LoggerHelper.getInstance().error("Cant load path: " + path, e);
            return;
        }
        URLClassLoader classLoader = new URLClassLoader(new URL[]{pathUrl});

        InputStream pluginInfo = classLoader.getResourceAsStream("plugin_info.xml");

        Map<String, String> info = null;
        try {
            info = parsePluginInfo(pluginInfo);
        } catch (Exception e) {
            LoggerHelper.getInstance().error("Cant parse target plugin info: " + path, e);
            return;
        }

        query.insert(DataBase.PLUGINS_TABLE);
        query.values("NULL, \"" + info.get(DataBase.PLUGINS_NAME_FIELD) + "\", \"" + info.get(DataBase.PLUGINS_DESC_FIELD) +"\"," +
                     "\"" + path + "\", \"" + info.get(DataBase.PLUGINS_CLASS_FIELD) + "\", 0");

        db.setQuery(query);

        db.updateDB();

        updatePluginsList();
    }

    public static void clearPluginsList() {
        QueryHelper query = new QueryHelper();
        DataBase db = DataBase.getInstance();

        query.delete(DataBase.PLUGINS_TABLE);

        db.setQuery(query);
        db.updateDB();

        updatePluginsList();
    }

    public static void selectPlugin(Integer index) {
        Map<String, Object> plugin = pluginsList.get(index);

        QueryHelper query = new QueryHelper();
        DataBase db = DataBase.getInstance();

        query.update(DataBase.PLUGINS_TABLE);
        query.set(DataBase.PLUGINS_DEFAULT_FIELD + "=0");

        db.setQuery(query);
        db.updateDB();

        query = new QueryHelper();
        query.update(DataBase.PLUGINS_TABLE);
        query.set(DataBase.PLUGINS_DEFAULT_FIELD + "=1");
        query.where("_id=" + plugin.get("_id"));

        db.setQuery(query);
        db.updateDB();

        updatePluginsList();
    }

    private static void updatePluginsList() {
        QueryHelper query = new QueryHelper();
        DataBase db = DataBase.getInstance();

        query.select("*");
        query.from(DataBase.PLUGINS_TABLE);

        db.setQuery(query);

        pluginsList = db.loadResult(pluginResultFormat);
    }

    private static Map<String, String> parsePluginInfo(InputStream pluginInfo) throws ParserConfigurationException, IOException, SAXException {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();

        Document doc = dBuilder.parse(pluginInfo);
        doc.getDocumentElement().normalize();

        doc.getDocumentElement().getNodeName();

        NodeList pluginName = doc.getElementsByTagName(DataBase.PLUGINS_NAME_FIELD);
        NodeList pluginDesc = doc.getElementsByTagName(DataBase.PLUGINS_DESC_FIELD);
        NodeList pluginClass = doc.getElementsByTagName(DataBase.PLUGINS_CLASS_FIELD);

        Map<String, String> info = new HashMap<String, String>();
        info.put(DataBase.PLUGINS_NAME_FIELD, pluginName.item(0).getTextContent());
        info.put(DataBase.PLUGINS_DESC_FIELD, pluginDesc.item(0).getTextContent());
        info.put(DataBase.PLUGINS_CLASS_FIELD, pluginClass.item(0).getTextContent());

        return info;
    }
}
